package org.bookie.framework.components;

import static org.bookie.core.services.impl.Listener.Event.USER_LOGGED_IN;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.tapestry.IPage;
import org.apache.tapestry.annotations.InjectObject;
import org.apache.tapestry.engine.state.ApplicationStateManager;
import org.apache.tapestry.event.PageBeginRenderListener;
import org.apache.tapestry.event.PageEvent;
import org.bookie.core.services.impl.Listener;
import org.bookie.framework.CommonBaseComponent;
import org.bookie.framework.CommonBasePage;
import org.bookie.framework.services.MenuBuilder;
import org.bookie.model.Role;
import org.bookie.model.User;

/**
 * Menu that is shown in the bookie pages
 * 
 * @author drobiazko
 * 
 */
public abstract class Menu extends CommonBaseComponent implements
		PageBeginRenderListener {

	/** CSS class name for the selected menu item */
	public static final String CSS_CURRENT = "current";

	/**
	 * Key used to store the application state object representing the selected
	 * page
	 */
	public static final String SELECTED_MENU_ITEM = "selectedMenuItem";

	/**
	 * Find the top level {@link MenuItem} to select.
	 * 
	 * @param items
	 *            list of items to look for MenuItem
	 * @return MenuItem corresponding to the current page, if the page is a top
	 *         level page. Otherwise the parent MenuItem of the
	 */
	private MenuItem findTopItem(List<MenuItem> items) {
		for (MenuItem item : items) {
			if (item.getPage().equals(getPage().getPageName())) {
				return item;
			}
			if (null != findTopItem(item.getChildren())) {
				return item;
			}
		}
		return null;
	}

	@InjectObject("service:tapestry.state.ApplicationStateManager")
	public abstract ApplicationStateManager getApplicationStateManager();

	/**
	 * Get the CSS for the item.
	 * 
	 * @param item
	 *            item to get CSS for
	 * @return CSS class name
	 */
	public String getCss(String item) {
		if (item.equals(getSelectedItem())) {
			return CSS_CURRENT;
		}
		return "";
	}

	/**
	 * Get the {@link MenuItem} of the current iteration.
	 * 
	 * @return instance of MenuItem
	 */
	public abstract MenuItem getItem();

	@InjectObject("service:bookie.framework.MenuBuilder")
	public abstract MenuBuilder getMenuBuilder();

	/**
	 * Get all {@link MenuItem}s the current {@link User} is allowed to
	 * request. This method returns a subset of the result returned by
	 * {@link #getItems()}. Pages requiring a {@link Role} which is not owned
	 * by the current {@link User} are filtered. This method should be used to
	 * render the UI.
	 * 
	 * @see #getItems()
	 */
	public List<MenuItem> getMenuItems() {
		return filterItems(getMenuBuilder().getItems());
	}
	
	
	private List<MenuItem> filterItems(List<MenuItem> items){
		List<MenuItem> result = new ArrayList<MenuItem>();
		Iterator<MenuItem> iter = items.iterator();
		while (iter.hasNext()) {
			MenuItem next = iter.next();
			IPage page = getRequestCycle().getPage(next.getPage());
			Role role = Role.GUEST;
			if (page instanceof CommonBasePage) {
				role = ((CommonBasePage) page).getRole();
			}
			if (getAuthenticationContext().isUserInRole(role)) {
				result.add(next);
			}
		}
		return result;
	}

	/**
	 * Get the name of the selected page.
	 * 
	 * @return name of the page
	 */
	public String getSelectedItem() {
		return (String) getApplicationStateManager().get(SELECTED_MENU_ITEM);
	}

	/**
	 * Get the children {@link MenuItem}s of the {@link MenuItem} of the
	 * selected page.
	 * 
	 * @return list of MenuItem
	 */
	public List<MenuItem> getSubItems() {
		for (MenuItem item : getMenuBuilder().getItems()) {
			if (getSelectedItem().equals(item.getPage())) {
				return filterItems(item.getChildren());
			}
		}
		return new ArrayList<MenuItem>();
	}

	/**
	 * @see org.apache.tapestry.event.PageBeginRenderListener#pageBeginRender(org.apache.tapestry.event.PageEvent)
	 */
	public void pageBeginRender(PageEvent event) {
		MenuItem pageItem = findTopItem(getMenuBuilder().getItems());
		if (null != pageItem) {
			setSelectedItem(pageItem.getPage());
		}
		if ("".equals(getSelectedItem())) {
			setSelectedItem(getMenuBuilder().getDefaultItem());
		}
	}

	/**
	 * Set the name of the selected page.
	 * 
	 * @param item
	 *            page name to set
	 */
	private void setSelectedItem(String item) {
		getApplicationStateManager().store(SELECTED_MENU_ITEM, item);
	}

	/**
	 * Updates itself when
	 */
	@Listener(USER_LOGGED_IN)
	public void updateMe() {
		getResponseBuilder().updateComponent(getClientId());
	}
}
